2025-09-22 Interesting bugs and where to find them

Unit Overview

Combination of labs and lectures

  • Get a high level overview of a technique, then get an opportunity to do it for real.

Problem sheets each week

  • Match exact style of exam questions
  • There are solutions
    • And source code
  • But better to talk about answers in the lab
  • Usually isn’t a right answer

Coursework:

  • Take a technique from the labs (ROP) and implement a general purpose tool to use it to automate an attack
  • Individual or group work
  • Marked according to how much you get done and how many people you had doing it

Mid-term:

  • Given a program you’ve not seen before
  • You have to exploit it (stack smashing and shellcode)
  • We watch you do it
  • If you can’t do it, there’s no way you can complete the coursework
  • Marked in front of you

Masters unit:

  • Lots of reading
  • Optional but:
    • Helps you explain ideas in more detail
    • May prefer their explanations

Assumptions

Software is built by building on other software (and hardware). Abstracting stuff away into libraries means you don’t need to know how the library actually works. But sometimes the way people assume abstractions work, and how they actually work are misaligned. This leads to opportunities for bugs and crashes.

Language spec might say undefined behaviour, but in practice that behaviour might be very much defined, and that behaviour can then be exploited.

Race Conditions

Fixed through locking/other sync stuff.

Access and Open

access() syscall checks accessibility of the file named by the path for the given permissions.

You can break it by symlinking a different file to the location being checked before the access, and reverting the symlink before the write.

Buffer Overflow

Q: How does your program know to crash when you read past an array? A: If you attempt to write/read from memory without permissions the MMU will trigger an exception to the OS and the program crashes with a segfault. But this is only if the memory you try to read is out of permissions bounds.

However, sometimes it doesn’t crash when you don’t go too far off the end. This is because the stuff beyond the array might be memory owned by the program. Such as stack pointers, return addresses, etc. This means we can write whatever we want there.

If we calculate the address of the buffer, store some code there, and overwrite the return address to be the start of the buffer, then we can execute whatever we want.

Fixes

  • Make addresses less predictable (ASLR)
  • Stick a canary in the stack and check it hasn’t been overwritten
  • Intel Branch Targets prevent indirect jumping to not endbr64

Format Strings

What if we print a format string without the arguments? Compiler warning. But it prints what’s next in the stack, as it thinks that’s the argument!

%08x is the format string for “hex number”. So if someone does printf on a variable without a format string, but instead the variable straight away, then you can make that variable contain as many %08xas you can, and it’ll start printing the contents of the stack.

the %n format string accepts a pointer to an int, and then fills it with how many characters have been written by that point in the format string. If you can force %n into a user-inputted string, then you can write custom ints.

Further Reading

Cards

Q: What’s an example of a syscall that’s vulnerable to race conditions? A: access() syscall checks accessibility of the file named by the path for the given permissions. You can break it by symlinking a different file to the location being checked before the access, and reverting the symlink before the write.

Q: What are fixes to stack smashing? A:

  • Make addresses less predictable (ASLR)
  • Stick a canary in the stack and check it hasn’t been overwritten
  • Intel Branch Targets prevent indirect jumping to not endbr64